#include "Shader.h"


//
//	Static - FromFiles - Vertex + Fragment
//
Shader* Shader::FromFiles(ifstream &vert, ifstream &frag) {
	Shader* shader = new Shader();
	shader->addVertexShader(vert);
	shader->addFragmentShader(frag);
	shader->linkProgram();
	return shader;
};



//
//	Static - FromFiles - Vertex + Fragment + Geometry
//
Shader* Shader::FromFiles(ifstream &vert, ifstream &frag, ifstream &geom) {
	Shader* shader = new Shader();
	shader->addVertexShader(vert);
	shader->addFragmentShader(frag);
	shader->addGeometryShader(geom);
	shader->linkProgram();
	return shader;
};



//
//	Static - FromStrings (String containing the source code of the shader program)
//
Shader* Shader::FromStrings(string vert, string frag, string geom) {
	Shader* shader = new Shader();
	shader->addVertexShader(vert, false);
	shader->addFragmentShader(frag, false);
	if(!geom.empty()) {
		shader->addGeometryShader(geom, false);
	}
	shader->linkProgram();
	return shader;
};



//
//	Static - FromFilenames
//
Shader* Shader::FromFilenames(string vert, string frag, string geom) {
	Shader* shader = new Shader();
	shader->addVertexShader(vert);
	shader->addFragmentShader(frag);
	if(!geom.empty()) {
		shader->addGeometryShader(geom);
	}
	shader->linkProgram();
	return shader;
};




//
//	Constructor
//
Shader::Shader() {
	program		= glCreateProgram();
	vertex		= NULL;
	fragment	= NULL;
	geometry	= NULL;
};



//
//	Destructor
//
Shader::~Shader(void) {
	// TODO : Cleanup OpenGL shaders
	if(vertex) {
		glDetachShader(program, vertex->id);
		delete vertex;
	}
	if(fragment) {
		glDetachShader(program, fragment->id);
		delete fragment;
	}
	if(geometry) {
		glDetachShader(program, geometry->id);
		delete geometry;
	}
	glDeleteProgram(program);
};



//
//	Link Program
//
void Shader::linkProgram() {
	glLinkProgram(program);
	GLint code;
	glGetProgramiv(program, GL_LINK_STATUS, &code);
	if(code != GL_TRUE) {
		throw runtime_error("SHADER PROGRAM LINKING FAILED");
	};
};


//
//	AddShaderObject - string
//
void Shader::addShaderObject(string s, GLenum type, bool isFilename) {
	switch(type) {
	case GL_VERTEX_SHADER :
		vertex		= new ShaderObject(s, GL_VERTEX_SHADER, isFilename);
		break;
	case GL_FRAGMENT_SHADER :
		fragment	= new ShaderObject(s, GL_FRAGMENT_SHADER, isFilename);
		break;
	case GL_GEOMETRY_SHADER :
		geometry	= new ShaderObject(s, GL_GEOMETRY_SHADER, isFilename);
		break;
	default : 
		throw runtime_error("INVALID SHADER TYPE");
		break;
	}
};


//
//	AddShaderObject - ifstream
//
void Shader::addShaderObject(ifstream &s, GLenum type) {
	switch(type) {
	case GL_VERTEX_SHADER :
		vertex		= new ShaderObject(s, GL_VERTEX_SHADER);
		break;
	case GL_FRAGMENT_SHADER :
		fragment	= new ShaderObject(s, GL_FRAGMENT_SHADER);
		break;
	case GL_GEOMETRY_SHADER :
		geometry	= new ShaderObject(s, GL_GEOMETRY_SHADER);
		break;
	default : 
		throw runtime_error("INVALID SHADER TYPE");
		break;
	}
};